package com.crazyimp.utilities;

import java.util.Stack;
import android.os.Handler;

public class Observer {
	private Stack<Observed> stack;
	private volatile boolean stop;	
	private Thread thread;
	private volatile boolean started = false;
	private volatile boolean modify = true;
	
	private Runnable observe = new Runnable(){
		@Override
		public void run() {
			while(!stop && !stack.isEmpty()){
				startModify();
				final Observed request = stack.pop();
				if (request != null){
					if (!stop && request.equal.isChanged(request.obj)){
						if (request.listener != null){
							request.handler.post(new Runnable(){
								@Override
								public void run() {
									request.listener.onDataChanged(request.obj);									
								}});	
							
						}
					}else 
						stack.add(request);
				}
				stopModify();
			}
		}
	};
	
	public Observer(){
		stack = new Stack<Observed>();		
		stop = false;
		thread = new Thread(observe);
		thread.setDaemon(true);
	}
	
	public Observed addObservation(Object obj, IsChanged equal, IDataChanged listener){
		Observed ob = new Observed();
		ob.obj = obj;
		ob.equal = equal;
		ob.listener = listener;
		ob.handler = new Handler();
		startModify();
		stack.add(ob);
		stopModify();
		if (!thread.isAlive()){
			startThread();
		}
		return ob;
	}
	
	public void addObservation(Observed ob){
		startModify();
		stack.add(ob);
		stopModify();
		if (!thread.isAlive()){
			startThread();
		}
	}
	
	public void removeObservation(Observed ob){
		startModify();
		stack.remove(ob);
		stopModify();
	}
	
	public void stop(){
		stop = true;
	}
	
	public void start(){
		if (!stack.isEmpty() && !thread.isAlive()){
			startThread();
		}
	}
	
	private void startThread(){
		if (started){
			thread = new Thread(observe);
			thread.setDaemon(true);
			thread.start();
		} else {
			started = true;
			thread.start();
		}
	}
	
	public synchronized void startModify(){
		while(!modify){}
		modify = false;
	}
	
	public void stopModify(){
		modify = true;
	}
}
